package cn.yunbamboos.helper;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.yunbamboos.Model;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationTargetException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;

public interface JSONHelper {

    Logger logger = LoggerFactory.getLogger(JSONHelper.class);

    default void put(JSONObject json, String key, Object value) {
        json.put(key, value);
    }

    default void putList(JSONObject json, String key, List<? extends Model> models) {
        JSONArray array = new JSONArray();
        if (CollectionUtil.isNotEmpty(models)) {
            for (Model model : models) {
                array.add(model.encode());
            }
        }
        json.put(key, array);
    }

    default Byte getByte(JSONObject json, String key) {
        return json.getByte(key);
    }

    default byte getByteValue(JSONObject json, String key) {
        return json.getByteValue(key);
    }

    default byte[] getBytes(JSONObject json, String key) {
        return json.getBytes(key);
    }

    default Short getShort(JSONObject json, String key, Short defaultValue) {
        return Optional.ofNullable(json.getShort(key)).orElse(defaultValue);
    }

    default short getShortValue(JSONObject json, String key) {
        return json.getShortValue(key);
    }

    default short getShortValue(JSONObject json, String key, short defaultValue) {
        short value = json.getShortValue(key);
        return value == 0 ? defaultValue : value;
    }

    default Integer getInteger(JSONObject json, String key) {
        return json.getInteger(key);
    }

    default Integer getInteger(JSONObject json, String key, Integer defaultValue) {
        return Optional.ofNullable(json.getInteger(key)).orElse(defaultValue);
    }

    default int getIntValue(JSONObject json, String key) {
        return json.getIntValue(key);
    }

    default int getIntValue(JSONObject json, String key, int defaultValue) {
        return json.getIntValue(key, defaultValue);
    }

    default Long getLong(JSONObject json, String key) {
        return json.getLong(key);
    }

    default Long getLong(JSONObject json, String key, Long defaultValue) {
        return Optional.ofNullable(json.getLong(key)).orElse(defaultValue);
    }

    default long getLongValue(JSONObject json, String key) {
        return json.getLongValue(key);
    }

    default long getLongValue(JSONObject json, String key, long defaultValue) {
        return json.getLongValue(key, defaultValue);
    }

    default Double getDouble(JSONObject json, String key) {
        return json.getDouble(key);
    }

    default Double getDouble(JSONObject json, String key, Double defaultValue) {
        return Optional.ofNullable(json.getDouble(key)).orElse(defaultValue);
    }

    default double getDoubleValue(JSONObject json, String key) {
        return json.getDoubleValue(key);
    }

    default Float getFloat(JSONObject json, String key) {
        return json.getFloat(key);
    }

    default Float getFloat(JSONObject json, String key, Float defaultValue) {
        return Optional.ofNullable(json.getFloat(key)).orElse(defaultValue);
    }

    default String getString(JSONObject json, String key) {
        return json.getString(key);
    }

    default String getString(JSONObject json, String key, String defaultValue) {
        return Optional.ofNullable(json.getString(key)).orElse(defaultValue);
    }

    default Boolean getBoolean(JSONObject json, String key) {
        return json.getBoolean(key);
    }

    default Boolean getBoolean(JSONObject json, String key, Boolean defaultValue) {
        return Optional.ofNullable(json.getBoolean(key)).orElse(defaultValue);
    }

    default boolean getBooleanValue(JSONObject json, String key) {
        return json.getBooleanValue(key);
    }

    default boolean getBooleanValue(JSONObject json, String key, boolean defaultValue) {
        return json.getBooleanValue(key, defaultValue);
    }

    default LocalDateTime getLocalDateTime(JSONObject json, String key, String format) {
        String str = json.getString(key);
        if(StrUtil.isNotBlank(str)){
            return LocalDateTimeUtil.parse(str, format);
        }
        return null;
    }

    default LocalDateTime getLocalDateTime(JSONObject json, String key, String format, LocalDateTime defaultValue) {
        String str = json.getString(key);
        if(StrUtil.isNotBlank(str)){
            return LocalDateTimeUtil.parse(str, format);
        }
        return defaultValue;
    }

    default LocalDateTime getLocalDateTime(JSONObject json, String key, String format, Supplier<LocalDateTime> supplier) {
        String str = json.getString(key);
        if(StrUtil.isNotBlank(str)){
            return LocalDateTimeUtil.parse(str, format);
        }
        return supplier.get();
    }

    default JSONArray getJSONArray(JSONObject json, String key) {
        return json.getJSONArray(key);
    }

    default JSONObject getJSONObject(JSONObject json, String key) {
        return json.getJSONObject(key);
    }

    default JSONObject getJSONObject(JSONObject json, String key, Supplier<JSONObject> supplier) {
        return Optional.ofNullable(json.getJSONObject(key)).orElseGet(supplier);
    }

    default List<Integer> getIntegerList(JSONObject json, String key) {
        if (!json.containsKey(key)) {
            return Collections.emptyList();
        }
        JSONArray array = json.getJSONArray(key);
        if (CollectionUtil.isEmpty(array)) {
            return Collections.emptyList();
        }
        List<Integer> list = new ArrayList<>(array.size());
        for (int i = 0; i < array.size(); i++) {
            list.add(array.getInteger(i));
        }
        return list;
    }

    default List<Long> getLongList(JSONObject json, String key) {
        if (!json.containsKey(key)) {
            return Collections.emptyList();
        }
        JSONArray array = json.getJSONArray(key);
        if (CollectionUtil.isEmpty(array)) {
            return Collections.emptyList();
        }
        List<Long> list = new ArrayList<>(array.size());
        for (int i = 0; i < array.size(); i++) {
            list.add(array.getLong(i));
        }
        return list;
    }

    default List<String> getStringList(JSONObject json, String key) {
        if (!json.containsKey(key)) {
            return Collections.emptyList();
        }
        JSONArray array = json.getJSONArray(key);
        if (CollectionUtil.isEmpty(array)) {
            return Collections.emptyList();
        }
        List<String> list = new ArrayList<>(array.size());
        for (int i = 0; i < array.size(); i++) {
            list.add(array.getString(i));
        }
        return list;
    }

    default List<Integer> getIntegerList(JSONObject json, String key, String attr) {
        List<Integer> list = new ArrayList<>();
        if (json.containsKey(key)) {
            JSONArray array = json.getJSONArray(key);
            for (int i = 0; i < array.size(); i++) {
                JSONObject item = array.getJSONObject(i);
                list.add(item.getInteger(attr));
            }
        }
        return list;
    }

    default List<Long> getLongList(JSONObject json, String key, String attr) {
        List<Long> list = new ArrayList<>();
        if (json.containsKey(key)) {
            JSONArray array = json.getJSONArray(key);
            for (int i = 0; i < array.size(); i++) {
                JSONObject item = array.getJSONObject(i);
                list.add(item.getLong(attr));
            }
        }
        return list;
    }

    default List<String> getStringList(JSONObject json, String key, String attr) {
        List<String> list = new ArrayList<>();
        if (json.containsKey(key)) {
            JSONArray array = json.getJSONArray(key);
            for (int i = 0; i < array.size(); i++) {
                JSONObject item = array.getJSONObject(i);
                list.add(item.getString(attr));
            }
        }
        return list;
    }

    default <T extends Model> T getModel(JSONObject json, String key, Class<T> clz) {
        if (!json.containsKey(key)) {
            return null;
        }
        JSONObject item = json.getJSONObject(key);
        if (ObjUtil.isNull(item)) {
            return null;
        }
        try {
            T model = clz.getDeclaredConstructor().newInstance();
            model.decode(item);
            return model;
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
                 NoSuchMethodException e) {
            logger.error("read model list error", e);
        }
        return null;
    }

    default <T extends Model> T getModel(JSONObject json, String key, Class<T> clz, Supplier<? extends T> supplier) {
        T model = this.getModel(json, key, clz);
        return Optional.ofNullable(model).orElseGet(supplier);
    }


    default <T extends Model> List<T> getModelList(JSONObject json, String key, Class<T> clz) {
        if (!json.containsKey(key)) {
            return Collections.emptyList();
        }
        JSONArray array = json.getJSONArray(key);
        if (CollectionUtil.isEmpty(array)) {
            return Collections.emptyList();
        }
        List<T> list = new ArrayList<>(array.size());
        for (int i = 0; i < array.size(); i++) {
            JSONObject item = array.getJSONObject(i);
            try {
                T model = clz.getDeclaredConstructor().newInstance();
                model.decode(item);
                list.add(model);
            } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
                     NoSuchMethodException e) {
                logger.error("read model list error", e);
            }
        }
        return list;
    }

}
